package com.lemonboxstudio.drawboard.widget

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PixelFormat
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.ViewTreeObserver
import com.lemonboxstudio.drawboard.data.Mode
import com.lemonboxstudio.drawboard.ktx.dp

class DrawingBoardView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : SurfaceView(context, attrs), ViewTreeObserver.OnGlobalLayoutListener, SurfaceHolder.Callback {

    private var startDraw = false

    private val path = Path()
    private val paint = Paint()

    private lateinit var canvas: Canvas
    private lateinit var bitmap: Bitmap

    val savePaths: ArrayList<Path> = arrayListOf()
    val deletePaths: ArrayList<Path> = arrayListOf()

    private var targetX = 0f
    private var targetY = 0f

    private var hasTouch = false

    private var mode: Mode = Mode.DRAW

    private var callBack: DrawCallBack? = null

    init {
        viewTreeObserver.addOnGlobalLayoutListener(this)
    }

    override fun onGlobalLayout() {
        viewTreeObserver.removeOnGlobalLayoutListener(this)
        init()
        initPaint()
        initCanvas()
    }

    private fun initCanvas() {
        bitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888)
        canvas = Canvas(bitmap)
    }

    private fun init() {
        initSurfaceView()
    }

    private fun initPaint() {
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 2.dp
        paint.color = Color.BLACK

    }

    private fun initSurfaceView() {
        holder.addCallback(this)
        holder.setFormat(PixelFormat.OPAQUE)
    }

    override fun surfaceCreated(p0: SurfaceHolder) {
        startDraw = true
        Thread {
            while (startDraw) {
                doDraw()
            }
        }.start()
    }

    inline val canUndo get() = savePaths.size > 0
    inline val canRedo get() = deletePaths.size > 0

    private fun doDraw() {
        val c = holder.lockCanvas()
        c.drawColor(Color.WHITE)
        c.drawBitmap(bitmap,0f,0f,null)
        if (mode == Mode.ERASER && hasTouch) {
            val size = 10.dp
            c.drawRect(targetX - size, targetY - size, targetX + size, targetY + size, Paint())
        }
        holder.unlockCanvasAndPost(c)
    }

    override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) {

    }

    override fun surfaceDestroyed(p0: SurfaceHolder) {
        startDraw = false
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        val x = event.x
        val y = event.y
        when(event.action) {
            MotionEvent.ACTION_DOWN -> {
                path.moveTo(x,y)
                hasTouch = true
                targetX = x
                targetY = y
            }
            MotionEvent.ACTION_MOVE -> {
                if (mode == Mode.ERASER) {
                    val size = 6.dp
                    paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
                    canvas.drawRect(targetX - size,targetY - size,targetX + size , targetY + size ,paint)
                    paint.xfermode = null
                } else {
                    path.lineTo(x,y)
                    canvas.drawPath(path,paint)
                }
                targetX = x
                targetY = y
            }
            MotionEvent.ACTION_UP -> {
                hasTouch = false
                val p = Path(path)
                savePaths.add(p)
                deletePaths.clear()
            }
        }
        return true
    }

    fun undo() {
        if (savePaths.size <= 0) return
        bitmap.eraseColor(Color.TRANSPARENT)
        path.reset()
        val tempPath = savePaths[savePaths.size - 1]
        deletePaths.add(tempPath)
        savePaths.remove(tempPath)
        savePaths.map {
            path.addPath(it)
        }
        canvas.drawPath(path, paint)
        callBack?.onUndoRedoStatusChanged()
    }

    fun redo() {
        if (deletePaths.size <= 0) return
        val tempPath = deletePaths[deletePaths.size -1]
        path.addPath(tempPath)
        canvas.drawPath(path, paint)
        savePaths.add(tempPath)
        deletePaths.remove(tempPath)
        callBack?.onUndoRedoStatusChanged()

    }

    fun clear() {
        bitmap.eraseColor(Color.TRANSPARENT)
        path.reset()
        savePaths.clear()
        deletePaths.clear()
    }

    fun setMode(mode: Mode) {
        if (this.mode != mode) {
            this.mode = mode
            if (mode == Mode.DRAW) {
                paint.xfermode = null
            } else {
                paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
            }
        }
    }

    fun getMode(): Mode {
        return mode
    }

    fun setOnDrawCallBack(callBack: DrawCallBack) {
        this.callBack = callBack
    }
}